
package com.joysuch.wwyt.bp.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.joysuch.wwyt.bp.BpSystemVideoSearchBean;
import com.joysuch.wwyt.bp.bean.BpSystemVideoAddBean;
import com.joysuch.wwyt.bp.entity.BpSystemVideo;
import com.joysuch.wwyt.bp.entity.dsl.QBpSystemVideo;
import com.joysuch.wwyt.bp.repository.BpSystemVideoDao;
import com.joysuch.wwyt.bp.service.BpSystemVideoService;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.constant.Constants;
import com.joysuch.wwyt.core.entity.BaseConfig;
import com.joysuch.wwyt.core.enums.BaseConfigCodes;
import com.joysuch.wwyt.core.enums.DeleteFlag;
import com.joysuch.wwyt.core.enums.StateEnum;
import com.joysuch.wwyt.core.enums.Status;
import com.joysuch.wwyt.core.exception.ApiBusinessException;
import com.joysuch.wwyt.core.service.BaseConfigService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.risk.entity.RiskDistrict;
import com.joysuch.wwyt.risk.repository.RiskDistrictDao;
import com.joysuch.wwyt.streampusher.VideoBrandEnum;
import com.joysuch.wwyt.streampusher.rtspUrlFormatter.RtspHistoryUrlFormatterFactory;
import com.joysuch.wwyt.streampusher.vo.StreamResponseBean;
import com.joysuch.wwyt.streampusher.vo.StreamVo;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;

import static com.joysuch.wwyt.bp.entity.dsl.QBpSystemVideo.bpSystemVideo;

@Service
@Slf4j
@Transactional
public class BpSystemVideoServiceImpl implements BpSystemVideoService {

    @Autowired
    private BpSystemVideoDao dao;
    @Autowired
    private RiskDistrictDao riskDistrictDao;
    @Autowired
    private BaseConfigService baseConfigService;

    @Override
    public Page<BpSystemVideoAddBean> list(Pageable page, BpSystemVideoSearchBean condition) {
        QBpSystemVideo q = bpSystemVideo;
        Predicate predicate = null;
        if (StringUtils.isNotBlank(condition.getName())) {
            predicate = ExpressionUtils.and(predicate, q.name.like("%" + condition.getName() + "%"));
        }
        if (condition.getBrand() != null) {
            predicate = ExpressionUtils.and(predicate, q.brand.eq(condition.getBrand()));
        }
        if (condition.getRiskDistrictId() != null) {
            predicate = ExpressionUtils.and(predicate, q.evaluation.eq(condition.getRiskDistrictId().toString()));
        }
        if (StringUtils.isNotBlank(condition.getDeviceClass())){
            predicate = ExpressionUtils.and(predicate,q.deviceClass.eq(condition.getDeviceClass()));
        }
        if (null != condition.getStatus() && 0 == condition.getStatus()){
            predicate = ExpressionUtils.and(predicate,q.region.isNull());
        }else if (null != condition.getStatus() && 1 == condition.getStatus()){
            predicate = ExpressionUtils.and(predicate,q.region.isNotNull());
        }
        Page<BpSystemVideo> all = predicate == null ? dao.findAll(page) : dao.findAll(predicate, page);
        return PageDataUtils.convertPageData(all, (bpSystemVideo) -> {
            BpSystemVideoAddBean b = new BpSystemVideoAddBean();
            BeanUtils.copyProperties(bpSystemVideo, b);
            b.setDateFormat("yyyy-MM-dd");
            b.setTimeFormat("mm:HH:dd");
            if (StringUtils.isNotBlank(bpSystemVideo.getEvaluation())) {
                Optional<RiskDistrict> districtOperation = riskDistrictDao.findById(new Long(bpSystemVideo.getEvaluation()));
                districtOperation.ifPresent(riskDistrict -> b.setEvaluationName(riskDistrict.getName()));
                b.setEvaluationId(bpSystemVideo.getEvaluation());
            }
            return b;
        });
    }

    @Override
    public List<BpSystemVideo> findAllHaveLocation() {
        return dao.findAllHaveLocation();
    }

    @Override
    public Long getNumForHaveLocation(Integer flag) {
        QBpSystemVideo q = bpSystemVideo;
        Predicate predicate = null;
        predicate = ExpressionUtils.and(predicate,q.deleteFlag.eq(DeleteFlag.EFFECTIVE.getCode()));
        if (null != flag && 1 == flag){
            predicate = ExpressionUtils.and(predicate,q.region.isNotNull());
        }
        return dao.count(predicate);
    }

    @Override
    public void add(BpSystemVideoAddBean bpSystemVideo) {
        BpSystemVideo b = new BpSystemVideo();
        BeanUtils.copyProperties(bpSystemVideo, b);
        b.setDeleteFlag("0");
        if (StringUtils.isBlank(bpSystemVideo.getDateFormat())) {
            b.setDateFormat("yyyy-MM-dd");
        } else {
            b.setDateFormat(bpSystemVideo.getDateFormat());
        }
        if (StringUtils.isBlank(bpSystemVideo.getTimeFormat())) {
            b.setTimeFormat("mm:HH:dd");
        } else {
            b.setTimeFormat(bpSystemVideo.getTimeFormat());
        }
        b.setEvaluation(bpSystemVideo.getEvaluationId());
        b.setCodeType(bpSystemVideo.getCodeType());
        BpSystemVideo save = dao.save(b);
        checkByNameAndBrand(save);
    }

    @Override
    public void update(BpSystemVideoAddBean bpSystemVideo) {
        BpSystemVideo one = dao.getOne(bpSystemVideo.getId());
        BeanUtils.copyProperties(bpSystemVideo, one);
        one.setEvaluation(bpSystemVideo.getEvaluationId());
        BpSystemVideo save = dao.save(one);
        checkByNameAndBrand(save);
    }

    private void checkByNameAndBrand(BpSystemVideo save) {
        int count = dao.countByNameAndBrand(save.getName(), save.getBrand());
        if (count > 1) {
            throw new IllegalArgumentException("该品牌下,视频设备名称已存在");
        }
    }

    @Override
    public void delete(Long id) {
        dao.deleteById(id);
    }

    @Override
    public BpSystemVideoAddBean getByid(Long id) throws ApiBusinessException {
        Optional<BpSystemVideo> optionalBpSystemVideo = dao.findById(id);
        if (!optionalBpSystemVideo.isPresent()){
            throw new ApiBusinessException(Status.VIDEO_NOT_FIND_ERROR);
        }
        BpSystemVideo bpSystemVideo = optionalBpSystemVideo.get();
        BpSystemVideoAddBean b = new BpSystemVideoAddBean();
        BeanUtils.copyProperties(bpSystemVideo, b);
        if (bpSystemVideo.getEvaluation() != null) {
            RiskDistrict one = riskDistrictDao.getOne(new Long(bpSystemVideo.getEvaluation()));
            b.setEvaluationName(one.getName());
            b.setEvaluationId(bpSystemVideo.getEvaluation());
        }
        return b;
    }

    /**
     * 打开视频流
     * @param id
     * @return
     * @throws ApiBusinessException
     */
    public ResultBean openStream(Long id, HttpServletRequest request) throws ApiBusinessException {
        BpSystemVideoAddBean video = getByid(id);
        if (video == null) {
            throw new ApiBusinessException(Status.VIDEO_STREAM_VIDEO_NOT_FOUND);
        }
        if (StrUtil.isEmpty(video.getRealTimeSite())) {
            throw new ApiBusinessException(Status.VIDEO_STREAM_LIVE_URL_NOT_CONFIG);
        }
        StreamVo streamVo = new StreamVo()
                .setRtspUrl(Arrays.asList(video.getRealTimeSite()))
                .setType(video.getCodeType())
                .setPalyType(StateEnum.PlayTypeEnum.LIVE.getValue());
        if (StringUtils.isNotBlank(video.getProtocol())) {
            streamVo.setProtocol(Integer.parseInt(video.getProtocol()));
        }

        try {
            String videoUrl = getVideoUrl();
            log.info("请求视频流播放服务器 IP: {}" , videoUrl);
            String res = HttpUtil.post(videoUrl + Constants.STREAM_OPENSTREAMS, JSONUtil.toJsonStr(streamVo));
            if (StringUtils.isEmpty(res)) {
                return ResultBean.wrapErrorResultDefault("开启视频流失败");
            }
            ResultBean resultBean = JSONUtil.toBean(res, ResultBean.class);
            return convertResultBean(resultBean,request);
        } catch (Exception e) {
            log.error("开启直播视频流失败",e);
            return ResultBean.wrapErrorResultDefault("开启直播视频流失败,信息如下：" + e.getMessage());
        }
    }


    public ResultBean openHistoryStream(Long id, Date startTime, Date endTime, HttpServletRequest request) throws ApiBusinessException {
        if (startTime == null || endTime == null) {
            throw new ApiBusinessException(Status.VIDEO_STREAM_START_OR_END_TIME_MISSING);
        }
        BpSystemVideoAddBean video = getByid(id);
        if (video == null) {
            throw new ApiBusinessException(Status.VIDEO_STREAM_VIDEO_NOT_FOUND);
        }
        if (StrUtil.isEmpty(video.getRealTimeSite())) {
            throw new ApiBusinessException(Status.VIDEO_STREAM_LIVE_URL_NOT_CONFIG);
        }
        String input = RtspHistoryUrlFormatterFactory.getFormatter(VideoBrandEnum.parseFromSystemVideo(video)).format(video.getHistorySite(), startTime, endTime);
        StreamVo streamVo = new StreamVo()
                .setRtspUrl(Arrays.asList(input))
                .setType(video.getCodeType())
                .setPalyType(StateEnum.PlayTypeEnum.HISTORY.getValue())
                .setStartTime(startTime)
                .setEndTime(endTime);
        try {
            String videoUrl = getVideoUrl();
            log.info("请求视频流播放服务器 IP: {}" , videoUrl);
            String res = HttpUtil.post(videoUrl + Constants.STREAM_OPENSTREAMS, JSONUtil.toJsonStr(streamVo));
            if (StringUtils.isEmpty(res)) {
                return ResultBean.wrapErrorResultDefault("开启历史视频流失败");
            }
            ResultBean resultBean = JSONUtil.toBean(res, ResultBean.class);
            return convertResultBean(resultBean,request);
        } catch (Exception e) {
            log.error("开启历史视频流出现异常:", e);
            return ResultBean.wrapErrorResultDefault("开启历史视频流失败,信息如下：" + e.getMessage());
        }
    }

    /**
     *  根据视频服务留地址配置，获取flv 实时流播放IP与端口： 默认本机:8888
     */

    private ResultBean convertResultBean(ResultBean resultBean, HttpServletRequest request) {
        StreamResponseBean streamBean = JSONUtil.toBean(JSONUtil.toJsonStr(resultBean.getData()), StreamResponseBean.class);
        String serverIp = request.getServerName();

        BaseConfig urlConfig = this.baseConfigService.findConfig(Context.getCurrentTenantId(), Context.getCompanyCode(), "videoReceiveStreamUrl");
        streamBean.setServerIp(request.getScheme() + "://" + serverIp + ":8888");
        if ((urlConfig != null) && (StringUtils.isNotEmpty(urlConfig.getValue()))){
            streamBean.setServerIp(urlConfig.getValue().trim());
        }
        resultBean.setData(streamBean);
        return resultBean;
    }

    // 获取视频流服务器URL配置
    private String getVideoUrl() {
        BaseConfig videoUrl = baseConfigService.findConfig(Context.getCurrentTenantId(), Context.getCompanyCode(), BaseConfigCodes.VIDEO_PUSH_STREAM_URL);
        return videoUrl== null || StringUtils.isEmpty(videoUrl.getValue()) ? "http://127.0.0.1:8070" :videoUrl.getValue();
    }

}
